package com.kara.woodAgent.common.handler;


import cn.hutool.core.util.ObjectUtil;

import com.alibaba.fastjson2.JSON;
import com.kara.woodAgent.common.exception.BizException;
import com.kara.woodAgent.common.model.Result;
import com.kara.woodAgent.common.model.ResultBytes;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;

import org.springframework.util.AntPathMatcher;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;

/**
 * @author szht
 * @version 1.0
 * @project tiny-admin
 * @description
 * @date 2024/10/11 11:31:37
 */
@Slf4j
@RestControllerAdvice
public class GlobalHandler implements ResponseBodyAdvice<Object> {


	/**
	 * 请求方式不支持
	 */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	public Result<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
	                                                        HttpServletRequest request) {
		String requestURI = request.getRequestURI();
		log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
		return Result.error(e.getMessage());
	}

	/**
	 * 业务异常
	 */
	@ExceptionHandler(BizException.class)
	public Result<Void> handleServiceException(BizException e, HttpServletRequest request) {
		log.error(e.getMessage(), e);
		Integer code = e.getCode();
		return ObjectUtil.isNotNull(code) ? Result.error(code, e.getMessage()) : Result.error(e.getMessage());
	}

	/**
	 * 拦截未知的运行时异常
	 */
	@ExceptionHandler(RuntimeException.class)
	public Result<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
		String requestURI = request.getRequestURI();
		log.error("请求地址'{}',发生未知异常.", requestURI, e);
		return Result.error(e.getMessage());
	}

	/**
	 * 系统异常
	 */
	@ExceptionHandler(Exception.class)
	public Result<Void> handleException(Exception e, HttpServletRequest request) {
		String requestURI = request.getRequestURI();
		log.error("请求地址'{}',发生系统异常.", requestURI, e);
		return Result.error(e.getMessage());
	}

	/**
	 * 自定义验证异常
	 */
	@ExceptionHandler(BindException.class)
	public Result<Void> handleBindException(BindException e) {
		log.error(e.getMessage(), e);
		String message = e.getAllErrors().get(0).getDefaultMessage();
		return Result.error(message);
	}

	/**
	 * 自定义验证异常
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public Result<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
		log.error(e.getMessage(), e);
		String message = e.getBindingResult().getFieldError().getDefaultMessage();
		return Result.error(message);
	}


	@Override
	public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
		return true;
	}

	private List<String> KNIFE4J_URI = Arrays.asList(
			"/doc.html",
			"/swagger-resources",
			"/swagger-resources/configuration",
			"/v3/api-docs",
			"/v2/api-docs",
			"/webjars/**");

	private boolean isKnife4jUrl(String uri) {
		AntPathMatcher pathMatcher = new AntPathMatcher();
		for (String s : KNIFE4J_URI) {
			if (pathMatcher.match(s, uri)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

		// 避免swagger失效
		if (isKnife4jUrl(request.getURI().getPath())) {
			return body;
		}


		if (body == null) {
			return Result.ok();
		}

		if (body instanceof ResultBytes) {
			writeBytes((ResultBytes) body, ((ServletServerHttpResponse) response).getServletResponse());
			return null;
		} else if (body instanceof Result) {
			return body;
		} else if (body instanceof String) {
			// String类型
			return Result.msg(body.toString());
		}
		// 否则直接包装成Result返回
		return Result.ok(body);
	}

	private void writeBytes(ResultBytes body, HttpServletResponse response) {
		byte[] bytes = body.getBytes();
		try {
			responseWrite(response, bytes, body.getName());
		} catch (Exception e) {
			throw new BizException(e.getMessage());
		}
	}


	private void responseWrite(HttpServletResponse response, byte[] bytes, String fileName) throws RuntimeException, IOException {
		fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
		response.setCharacterEncoding("utf-8");
		response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
		response.setHeader("Content-disposition", "attachment;filename='" + fileName + "'");
		response.setHeader("Content-Length", bytes.length + "");
		try (ServletOutputStream outputStream = response.getOutputStream()) {
			outputStream.write(bytes);
			outputStream.flush();
		}
	}


}
